/*
 * This file is part of aKoustiK.

 * aKoustiK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * aKoustiK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with aKoustiK.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <KLocale>

#include <QtCore/QDebug>
#include <QtCore/QList>
#include <QtCore/QMimeData>
#include <QtCore/QSet>

#include <kore.h>
#include <mediaseeker.h>
#include <ui/player/playlisttablemodel.h>
#include <storage/internalstorage.h>

#include <QtCore/QThread>

PlaylistTableModel::PlaylistTableModel(QObject *parent) : QAbstractTableModel(parent) {
    qDebug("PlaylistTableModel: constructor invoked");
    mPlaylist = Kore::instance().playlist();
    setSupportedDragActions(Qt::MoveAction);

    // listening for changes in current item:
    connect(Kore::instance().player(), SIGNAL(currentItemChanged(int,int)), SLOT(itemUpdated(int,int)));
}

PlaylistTableModel::~PlaylistTableModel() {
    qDebug("PlaylistTableModel: destructor invoked");
}

int PlaylistTableModel::rowCount(const QModelIndex &parent) const {
    if (parent.isValid()) {
        return 0;
    }
    return mPlaylist->size();
}

int PlaylistTableModel::columnCount(const QModelIndex &parent) const {
    if (parent.isValid()) {
        return 0;
    }
    return 5;
}

QVariant PlaylistTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
        switch (section) {
        case 0: return i18n("Title");
        case 1: return i18n("Artist");
        case 2: return i18n("Album");
        case 3: return i18n("Year");
        case 4: return i18n("Duration");
        }
    }
    return QAbstractTableModel::headerData(section, orientation, role);
}

QVariant PlaylistTableModel::data(const QModelIndex &index, int role) const {
    // if not valid - return empty QVariant
    // if checking state - return empty QVariant or otherwise checkboxes will be visible
    //qDebug("PlaylistTableModel::data: playlist size: %i, requested item: %i", mPlaylist->size(), index.row());
    if (!index.isValid() || role == Qt::CheckStateRole || index.row() >= mPlaylist->size()) {
        return QVariant();
    }

    Item* item = mPlaylist->at(index.row());

    if (item == 0) {
        return QVariant();
    }

    if (role == Qt::DisplayRole) {
        QString value;
        switch (index.column()) {
        case 0: {
                value = item->title();
                break;
            }
        case 1: {
                value = item->artist();
                break;
            }
        case 2: {
                value = item->album();
                break;
            }
        case 3: {
                value = item->year();
                break;
            }
        case 4: {
                value = item->duration();
                break;
            }
        }
        return value;
    }

    if (role == Qt::ToolTipRole) {

        // TODO it's the same code as in tray.cxx, we should move it to common place

        const QString& artist = item->artist();
        const QString& album = item->album();
        const QString& year = item->year();
        const QString& time = item->duration();

        QString string;
        string += "<center style=\"text-align:center; white-space:nowrap;\">";
        string += "<b>";
        string += item->title();
        string += "</b>";
        if (!time.isEmpty()) {
            string += " (";
            string += time;
            string += ")";
        }
        if (!artist.isEmpty()) {
            string += "<br/>";
            string += artist;
            if (!album.isEmpty()) {
                string += " - ";
                string += album;
                if (!year.isEmpty()) {
                    string += " (";
                    string += year;
                    string += ")";
                }
            }
        }
        string += "</center>";

        return string;
    }

    return QVariant();
}

void PlaylistTableModel::emitDataChanged(int index) {
    if (index < 0) {
        return;
    }
    QModelIndex start = createIndex(index, 0);
    QModelIndex end = createIndex(index, 4);
    emit dataChanged(start, end);
}

void PlaylistTableModel::itemUpdated(int newIndex, int oldIndex) {
    emitDataChanged(oldIndex);
    emitDataChanged(newIndex);
}

void PlaylistTableModel::deleteItems(const QModelIndexList &list) {
    if (list.isEmpty()) {
        return;
    }
    // TODO maybe use background thread for this work?
    // for some reason we can can single index more than once, so we must get only unique:
    QSet<int> indexesSet;
    for (int i = 0; i < list.size(); i++) {
        const QModelIndex& modelIndex = list.at(i);
        if (modelIndex.isValid()) {
            indexesSet.insert(modelIndex.row());
        }
    }
    QList<int> indexesList = indexesSet.toList();

    // we can't just mark first and last, it must be continuour list
    // so we'll better remove each one instead of additional logic operations:
    qSort(indexesList);
    for (int i = indexesList.size() - 1; i >= 0; i--) {
        int index = indexesList.at(i);
        emit beginRemoveRows(QModelIndex(), index, index);
        mPlaylist->removeItem(index);
        emit endRemoveRows();
    }
}

Qt::DropActions PlaylistTableModel::supportedDropActions() const {
    //qDebug("PlaylistTableModel::supportedDropActions()");
    return Qt::CopyAction | Qt::MoveAction;
}

QStringList PlaylistTableModel::mimeTypes() const {
    //qDebug("PlaylistTableModel::mimeTypes()");
    return (QStringList() << "text/uri-list");
}

Qt::ItemFlags PlaylistTableModel::flags(const QModelIndex &index) const {
    //qDebug("PlaylistTableModel: flags");
    Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
    // if item is valid - it's real table item, it can be dragged but cannot be dropped on:
    if (index.isValid()) {
        //qDebug("PlaylistTableModel: flags: DragEnabled");
        return Qt::ItemIsDragEnabled | defaultFlags;
    }
    //qDebug("PlaylistTableModel: flags: DropEnabled");
    // if we are still here - it's not valid item, so only drop is possible because we are between items:
    return Qt::ItemIsDropEnabled | defaultFlags;
}

bool PlaylistTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
    qDebug("PlaylistTableModel::dropMimeData()");
    Q_UNUSED(column);
    Q_UNUSED(parent);

    if (row < 0 || row > mPlaylist->size()) {
        row = mPlaylist->size();
    }

    if (data->hasUrls()) {
        const QList<QUrl>& urls = data->urls();
        MediaSeeker* mediaSeeker = new MediaSeeker(urls);
        connect(mediaSeeker, SIGNAL(beforePlaylistAppend(int,int)), SLOT(beforePlaylistAppend(int,int)));
        connect(mediaSeeker, SIGNAL(afterPlaylistAppend()), SLOT(afterPlaylistAppend()));
        mediaSeeker->start();
        return true;
    }

    return false;
}

Qt::DropActions PlaylistTableModel::supportedDragActions() const {
    //qDebug("PlaylistTableModel::supportedDragActions()");
    return QAbstractTableModel::supportedDragActions();
}

QMimeData* PlaylistTableModel::mimeData(const QModelIndexList &indexes) const {
    //qDebug("PlaylistTableModel::mimeData()");
    return QAbstractTableModel::mimeData(indexes);
}

void PlaylistTableModel::beforePlaylistAppend(int index, int size) {
    //qDebug("PlaylistTableModel::beforePlaylistAppend(%i, %i)", index, size);
    //qDebug() << "PlaylistTableModel::beforePlaylistAppend(int) in thread: " << QThread::currentThread();
    beginInsertRows(QModelIndex(), index, index + size - 1);
}

void PlaylistTableModel::afterPlaylistAppend() {
    //qDebug() << "PlaylistTableModel::afterPlaylistAppend() in thread: " << QThread::currentThread();
    endInsertRows();
}
